package controllers

import (
	"crypto/sha256"
	"fmt"
	"time"

	"gitee.com/bitwormhole/mails-pnp-driver/lib/common/vo"
	"gitee.com/bitwormhole/mails-pnp-driver/lib/pnp"

	"github.com/gin-gonic/gin"
	"github.com/starter-go/base/lang"
	"github.com/starter-go/libgin"
	"github.com/starter-go/vlog"
)

// MessageController ...
type MessageController struct {

	//starter:component
	_as func(libgin.Controller) //starter:as(".")

	Sender     libgin.Responder //starter:inject("#")
	PNPService pnp.Service      //starter:inject("#")

	// MailsService mails.Service    //starter:inject("#")

	WantPullTokenSum string //starter:inject("${mails.pnp.server.pull.want-token-sha256sum}")
	WantPushTokenSum string //starter:inject("${mails.pnp.server.push.want-token-sha256sum}")

}

func (inst *MessageController) _impl() libgin.Controller {
	return inst
}

// Registration ...
func (inst *MessageController) Registration() *libgin.ControllerRegistration {
	return &libgin.ControllerRegistration{Route: inst.route}
}

func (inst *MessageController) route(rp libgin.RouterProxy) error {

	rp = rp.For("/api/pnp/messages")

	rp.POST("pull", inst.handlePostMessagePull)
	rp.POST("push", inst.handlePostMessagePush)

	// rp.POST("test", inst.handlePostTestMessage)
	// rp.PUT(":id", inst.handle)
	// rp.DELETE(":id", inst.handle)
	// rp.GET("", inst.handle)
	// rp.GET(":id", inst.handleGetOne)

	return nil
}

func (inst *MessageController) handle(c *gin.Context) {
	req := &myExampleRequest{
		context:       c,
		controller:    inst,
		wantRequestID: false,
	}
	req.execute(req.doNOP)
}

func (inst *MessageController) handlePostMessagePull(c *gin.Context) {
	req := &myExampleRequest{
		context:         c,
		controller:      inst,
		wantRequestID:   false,
		wantRequestBody: false,
		wantCheckToken:  true,

		wantTokenSum: lang.Hex(inst.WantPullTokenSum),
	}
	req.execute(req.doPostPull)
}

func (inst *MessageController) handlePostMessagePush(c *gin.Context) {
	req := &myExampleRequest{
		context:         c,
		controller:      inst,
		wantRequestID:   false,
		wantRequestBody: true,
		wantCheckToken:  true,

		wantTokenSum: lang.Hex(inst.WantPushTokenSum),
	}
	req.execute(req.doPostPush)
}

// func (inst *MessageController) handlePostTestMessage(c *gin.Context) {
// 	req := &myExampleRequest{
// 		context:         c,
// 		controller:      inst,
// 		wantRequestID:   false,
// 		wantRequestBody: true,
// 		wantCheckToken:  true,
// 	}
// 	req.execute(req.doPostTest)
// }

////////////////////////////////////////////////////////////////////////////////

type myExampleRequest struct {
	context    *gin.Context
	controller *MessageController

	wantRequestID   bool
	wantRequestBody bool
	wantCheckToken  bool

	wantTokenSum lang.Hex

	// id    dxo.ExampleID
	body1 vo.Message
	body2 vo.Message
}

func (inst *myExampleRequest) open() error {

	c := inst.context

	if inst.wantRequestID {
		// idstr := c.Param("id")
		// idnum, err := strconv.ParseInt(idstr, 10, 64)
		// if err != nil {
		// 	return err
		// }
		// inst.id = dxo.ExampleID(idnum)
	}

	if inst.wantRequestBody {
		err := c.BindJSON(&inst.body1)
		if err != nil {
			return err
		}
	}

	if inst.wantCheckToken {
		err := inst.checkToken2()
		if err != nil {
			time.Sleep(time.Second)
			return err
		}
	}

	return nil
}

func (inst *myExampleRequest) send(err error) {
	data := &inst.body2
	code := inst.body2.Status
	resp := new(libgin.Response)
	resp.Context = inst.context
	resp.Error = err
	resp.Data = data
	resp.Status = code
	inst.controller.Sender.Send(resp)
}

func (inst *myExampleRequest) execute(fn func() error) {
	err := inst.open()
	if err == nil {
		err = fn()
	}
	inst.send(err)
}

func (inst *myExampleRequest) checkToken2() error {

	token := inst.context.GetHeader("token")
	sum := sha256.Sum256([]byte(token))

	want := inst.wantTokenSum
	have := lang.HexFromBytes(sum[:])

	if want == have {
		return nil
	}
	vlog.Error("bad http.request.header.token sum [want:%s have:%s]", want, have)
	time.Sleep(time.Second)
	return fmt.Errorf("bad token")
}

// func (inst *myExampleRequest) getWantSum() lang.Hex {
// 	str := inst.controller.WantTokenSum
// 	hex := lang.Hex(str)
// 	return hex
// }

func (inst *myExampleRequest) doNOP() error {
	return nil
}

// func (inst *myExampleRequest) doPostTest() error {
// 	ctx := inst.context
// 	ser := inst.controller.MailsService
// 	src := &inst.body1
// 	dst := &mails.Message{}
// 	for _, item := range src.Items {
// 		to := item.ToAddr
// 		dst.FromUser = item.FromUser
// 		dst.ToUser = item.ToUser
// 		dst.FromAddress = mails.Address(item.FromAddr)
// 		dst.ToAddresses = []mails.Address{mails.Address(to)}
// 		dst.Title = item.Title
// 		dst.ContentType = item.ContentType
// 		dst.Content = []byte(item.Content)
// 	}
// 	return ser.Send(ctx, dst)
// }

func (inst *myExampleRequest) doPostPull() error {

	ctx := inst.context
	timeout := time.Second * 600

	ser := inst.controller.PNPService
	src, err := ser.Pull(ctx, timeout)
	if err != nil {
		return err
	}

	dst := &inst.body2
	dst.Items = src.Items
	return nil
}

func (inst *myExampleRequest) doPostPush() error {
	ctx := inst.context
	ser := inst.controller.PNPService
	msg := &inst.body1
	return ser.Push(ctx, msg)
}
